package edu.sdbi.judge.util;

import edu.sdbi.judge.domain.entity.CopyJudge;
import lombok.SneakyThrows;

import java.io.*;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.zip.*;

public class PlagiarismCheckerUtil {
    // 标准答案文件路径（需提前设置）
    private static final String ANSWER1_PATH = "G:\\lxm\\1.授课\\Java EE\\项目大作业\\抄袭判断\\NewsMapper.java";
    private static final String ANSWER2_PATH = "G:\\lxm\\1.授课\\Java EE\\项目大作业\\抄袭判断\\NewsService.java";
    private static final String ANSWER3_PATH = "G:\\lxm\\1.授课\\Java EE\\项目大作业\\抄袭判断\\NewsController.java";

    // 读取标准答案
    private static final Set<String> answer1;
    private static final Set<String> answer2;
    private static final Set<String> answer3;
    static {
        try {
            answer1 = readFileToString(ANSWER1_PATH);
            answer2 = readFileToString(ANSWER2_PATH);
            answer3 = readFileToString(ANSWER3_PATH);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @SneakyThrows
    public static CopyJudge judge(File file, Charset charset) {
        String studentId = file.getName().replace(".jar", "").split("-")[0];
        String filePath = "G:\\lxm\\1.授课\\Java EE\\项目大作业\\临时";
        unzip(file, filePath, charset);
        File tempDir = new File(filePath);

        // 查找目标文件
        File studentFile1 = findFile(tempDir, "NewsMapper.java");
        File studentFile2 = findFile(tempDir, "NewsService.java");
        File studentFile3 = findFile(tempDir, "NewsController.java");

        // 计算相似度
        double similarity1 = calculateSimilarity(studentFile1, answer1);
        double similarity2 = calculateSimilarity(studentFile2, answer2);
        double similarity3 = calculateSimilarity(studentFile3, answer3);
        CopyJudge judge = new CopyJudge();
        judge.setSimilarity1(similarity1);
        judge.setSimilarity2(similarity2);
        judge.setSimilarity3(similarity3);
        judge.setSimilarity(similarity1 > 0.5 || similarity2 > 0.5 || similarity3 > 0.5
                || similarity1 + similarity2 + similarity3 > 1);
        judge.setId(studentId);
        deleteDirectory(tempDir); // 清理临时文件
        return judge;
    }

    private static void unzip(File zipFile, String outputDir, Charset charset) throws IOException {
        // 检查输入参数有效性
        if (zipFile == null || !zipFile.exists()) {
            throw new FileNotFoundException("ZIP文件不存在: " + zipFile);
        }
        if (outputDir == null || outputDir.trim().isEmpty()) {
            throw new IllegalArgumentException("输出目录不能为空");
        }

        // 创建输出目录（如果不存在）
        Path outputPath = Paths.get(outputDir);
        if (!Files.exists(outputPath)) {
            Files.createDirectories(outputPath);
        }

        // 显式指定UTF-8编码处理文件名
        try (ZipInputStream zis = new ZipInputStream(
                new BufferedInputStream(new FileInputStream(zipFile)), charset)) {

            ZipEntry entry;
            byte[] buffer = new byte[8192];

            // 修复点：使用显式UTF-8编码读取条目
            while ((entry = zis.getNextEntry()) != null) {
                String entryName = entry.getName();

                // 防止ZIP路径遍历攻击
                Path targetPath = outputPath.resolve(entryName).normalize();
                if (!targetPath.startsWith(outputPath)) {
                    throw new IOException("非法ZIP条目: " + entryName);
                }

                // 处理目录
                if (entry.isDirectory()) {
                    Files.createDirectories(targetPath);
                    continue;
                }

                // 确保父目录存在
                Path parent = targetPath.getParent();
                if (parent != null && !Files.exists(parent)) {
                    Files.createDirectories(parent);
                }

                // 写入文件
                try (OutputStream fos = Files.newOutputStream(targetPath,
                        StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
                    int bytesRead;
                    while ((bytesRead = zis.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                }
                // 关闭当前条目
                zis.closeEntry();
            }
        } catch (IOException e) {
            throw new IOException("解压失败: " + zipFile.getName(), e);
        }
    }

    private static File findFile(File dir, String fileName) {
        Queue<File> queue = new LinkedList<>();
        queue.add(dir);

        while (!queue.isEmpty()) {
            File current = queue.poll();
            if (current.isDirectory()) {
                Collections.addAll(queue, Objects.requireNonNull(current.listFiles()));
            } else if (current.getName().equals(fileName)) {
                return current;
            }
        }
        return null; // 未找到文件
    }

    private static Set<String> readFileToString(String path) throws IOException {
        Set<String> set = new HashSet<>();
        try (BufferedReader reader = new BufferedReader(new FileReader(path))) {
            String line;
            while ((line = reader.readLine()) != null) {
                if (!line.trim().isEmpty()) {
                    set.add(line.trim().toLowerCase());
                }
            }
        }
        return set;
    }

    private static double calculateSimilarity(File studentFile, Set<String> answerContent) {
        if (studentFile == null || answerContent.isEmpty()) return 0.0;

        int count = 0;
        try {
            Set<String> studentContents = readFileToString(studentFile.getAbsolutePath());
            for (String content : studentContents) {
                if (answerContent.contains(content)) {
                    count++;
                }
            }
        } catch (IOException e) {
            System.err.println("读取文件失败: " + studentFile.getName());
            return 0.0;
        }
        return count * 1.0 / answerContent.size();
    }


    private static void deleteDirectory(File dir) {
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                if (file.isDirectory()) {
                    deleteDirectory(file);
                } else {
                    file.delete();
                }
            }
        }
        dir.delete();
    }
}